﻿#include "chatclient.h"
#include <QLogger>
#include <QThread>
#include <QtDebug>

QTextStream cout(stdout);

TcpChatter::TcpChatter(const QString &id, QObject *parent) : QObject(parent) {
  init();
  setID(id);
}

TcpChatter::~TcpChatter() {
  if (isInit) {
    Socket->deleteLater();
  }
}

void TcpChatter::setGroup(const QString &groupname) {
  if (!groupname.isEmpty()) {
    Group = groupname;
    updateGroup();
  } else {
    Warning("Gourp name cannot be empty");
  }
}

void TcpChatter::setID(const QString &id) {
  if (!id.contains(' ') && !id.contains('$')) {
    ID = id;
    if (isConnectServer) {
      Socket->disconnectFromHost();

      Socket->connectToHost(ServerIP, ServerPort);
      if (Socket->waitForConnected(500)) {
        writeToSocket(ID);
        connect(Socket, &QTcpSocket::readyRead, this, &TcpChatter::readTcpMesg);
        isConnectServer = true;
      } else {
        Warning("Cannot connect to server");
      }
    }
  } else {
    Warning("ID cannot contains SPACE and $");
  }
}

void TcpChatter::init() {
  if (isInit)
    return;
  Socket = new QTcpSocket();
  isInit = true;
}

bool TcpChatter::connectToServer(const QString &ip, const int &port) {

  Socket->connectToHost(ip, port);

  if (Socket->waitForConnected(500)) {
    ServerIP = ip;
    ServerPort = port;
    writeToSocket(ID);
    connect(Socket, &QTcpSocket::readyRead, this, &TcpChatter::readTcpMesg);
    updateGroup(); // FIXME：
                   // 在这里的updateGroup似乎是会失效的，只有外部重新调用setGroup才能成功配置
    isConnectServer = true;

    return true;
  } else {
    QLogger::Debug("Cannot connect to server");
    return false;
  }
}

void TcpChatter::sendTo(const QString &mesg, const QString &id) {
  if (CheckMesg(mesg)) {
    writeToSocket("$sendTo " + id + "$" + mesg);
  }
}

void TcpChatter::sendToGroup(const QString &mesg, const QString &group) {
  if (CheckMesg(mesg)) {
    writeToSocket("$sendToGroup " + group + "$" + mesg);
  }
}

void TcpChatter::broadcast(const QString &mesg) {
  if (CheckMesg(mesg)) {
    writeToSocket("$broadcast$" + mesg);
  }
}

void TcpChatter::updateGroup() {
  if (isConnectServer && !Group.isEmpty()) {
    writeToSocket("$setGroup " + Group + "$");
  }
}

void TcpChatter::sendCommandTo(const QString &cmd, const QStringList &param,
                               const QString &id) {
  if (CheckMesg(cmd)) {
    writeToSocket("$sendCmdTo " + id + "$" + cmd + "$" + param.join("$"));
  }
}
void TcpChatter::sendCommandToGroup(const QString &cmd,
                                    const QStringList &param,
                                    const QString &group) {
  if (isConnectServer) {
    writeToSocket("$sendCmdToGrp " + group + "$" + cmd + "$" + param.join("$"));
  }
}

bool TcpChatter::CheckMesg(const QString &mesg) { return !mesg.contains('$'); }

void TcpChatter::parse(const QByteArray &data) {
  /**************************************************/
  // parse
  QRegExp rx("^(\\$[^\\$]*\\$)?(.*)");
  rx.exactMatch(QString::fromUtf8(data));
  QStringList list = rx.capturedTexts();
  QString header = list.at(1);
  QString info = list.at(2);
  // remove $
  header.remove(0, 1);
  header.remove(header.length() - 1, 1);
  QString type = header.split(" ").at(0), sender = header.split(" ").at(1);
  if (type == "text") {
    emit message(info);
    emit message(info, sender);
    if (MesgCallbackMap.contains(info)) {
      for (auto it = MesgCallbackMap[info].begin();
           it != MesgCallbackMap[info].end(); ++it) {
        (*it)();
      }
    }
  } else if (type == "cmd") {
    QStringList param = info.split('$');
    QString cmd = param.front();
    param.pop_front();
    emit command(cmd, param);
    emit command(cmd, param, sender);
    if (CmdCallbackMap.contains(cmd)) {
      foreach (auto funtor, CmdCallbackMap[cmd]) {
        funtor(param);
      }
    }
  } else {
    Warning("WTF? update chatter and server");
  }
}

QString TcpChatter::printPrefix() { return "TCP Chatter " + ID + "::"; }

void TcpChatter::Info(const QString &info) {
  QLogger::Info(printPrefix() + info);
}

void TcpChatter::Warning(const QString &info) {
  QLogger::Warn(printPrefix() + info);
}

void TcpChatter::writeToSocket(const QString &str) {
  Socket->write(pack.pack(str.toUtf8()));
}

void TcpChatter::readTcpMesg() {
  pack << Socket->readAll();
  while (pack) {
    QByteArray out;
    pack >> out;
    parse(out);
  }
}

void TcpChatter::regMesgCallback(const QString &mesg, MesgCallBackType func) {
  MesgCallbackMap[mesg].append(func);
}
void TcpChatter::cleanMesgCallback() { MesgCallbackMap.clear(); }

void TcpChatter::regCmdCallback(const QString &cmd, CmdCallBackType func) {
  CmdCallbackMap[cmd].append(func);
}

void TcpChatter::cleanCmdCallback() { CmdCallbackMap.clear(); }
